#include "wc3jassvm.h"
#include "Extern_global_Variable.h"

PJassThreadLocal GetJassThreadLocal()
{
	return PJassThreadLocal(GetWc3Prop(piJassEnv));
};

Boolean GetScriptDataTypeByLetter(Char &Letter, TScriptDataType &DataType)
{
	Boolean Result;
	Result = TRUE;

	switch (Letter)
	{
		case SCRIPT_TYPE_LETTER_VOID: DataType = stVoid; break;
		case SCRIPT_TYPE_LETTER_CODE: DataType = stCode; break;
		case SCRIPT_TYPE_LETTER_INTEGER: DataType = stInteger; break;
		case SCRIPT_TYPE_LETTER_REAL: DataType = stReal; break;
		case SCRIPT_TYPE_LETTER_STRING: DataType = stString; break;
		case SCRIPT_TYPE_LETTER_HANDLE: DataType = stHandle; break;
		case SCRIPT_TYPE_LETTER_BOOLEAN: DataType = stBoolean; break;
		default: Result = FALSE;
	};
	return Result;
};

procedure wc3jassvm_OnInit()
{
	PtrUInt(_RegisterNative) = (PtrUInt)GameDll + 0x455110;
	PtrUInt(_Compile) = (PtrUInt)GameDll + 0x458900;
	PtrUInt(_FreeScript) = (PtrUInt)GameDll + 0x455800;
	PtrUInt(_CreateInstance) = (PtrUInt)GameDll + 0x455760;
	PtrUInt(_RegisterInstance) = (PtrUInt)GameDll + 0x44D960;
	PtrUInt(_FreeInstance) = (PtrUInt)GameDll + 0x453C50;
	PtrUInt(_RunInstance) = (PtrUInt)GameDll + 0x44B640;
	PtrUInt(_CloneInstance) = (PtrUInt)GameDll + 0x44D9B0;
	PtrUInt(_Instance_Run) = (PtrUInt)GameDll + 0x45E9D0;
	PtrUInt(_Instance_CallCode) = (PtrUInt)GameDll + 0x45FA30;
	PtrUInt(_Instance_ExecuteFunctionWithOptionalInit) = (PtrUInt)GameDll + 0x45F8B0;
	PtrUInt(_ExecuteFunctionByHandle) = (PtrUInt)GameDll + 0x44B6A0;
	PtrUInt(_Instance_GetStringHandle) = (PtrUInt)GameDll + 0x45D210;
	JassArray_VMT = Pointer(GameDll + 0x951060);
};

TFrameStack::TFrameStack()
{
	Integer vvi;

	Capacity = sizeof(TScriptData) * 32;
	for (vvi = 0; vvi <= 32 - 1; vvi++)
	{
		Items[vvi] = PScriptData( Allocate((PtrUInt)sizeof(TScriptData)) );
		(*Items[vvi]).TScriptData::TScriptData();
	};
	Length = 0;
};

TFrameStack::~TFrameStack()
{
	Integer vvi;

	for (vvi = 0; vvi <= 32 - 1; vvi++)
	{
		(*Items[vvi]).~TScriptData();
		Free(Items[vvi]);
	};
};

TFrameStackEnumerator TFrameStack::GetEnumerator()
{
	return TFrameStackEnumerator(this);
};

Boolean TFrameStackEnumerator::MoveNext()
{
	FPosition++;
	return FPosition < (*FOwner).Length;
};

PScriptData TFrameStackEnumerator::GetCurrent()
{
	return (*FOwner).Items[FPosition];
};

procedure TFrameStackEnumerator::Reset()
{
	FPosition = (Dword)(-1);
};

TFrameStackEnumerator::TFrameStackEnumerator(PFrameStack AOwner)
{
	FOwner = AOwner;
	Reset();
};

TScriptData::TScriptData()
{
	HashKeyData.PageNode.Clear();
	HashKeyData.ItemsNode.Clear();
	InitializedType = stVoid;
	_Type = stVoid;
	Value = 0;
	IsArgument = FALSE;
};

TScriptData::~TScriptData()
{

};

//template <class THashKeyData, class TScriptData, class TConstantStringHashKeyManager>
//TFrame<THashKeyData,TScriptData,TConstantStringHashKeyManager>::TFrame()
TFrame::TFrame()
{
	Node.Clear();
	Stack.TFrameStack::TFrameStack();
	Locals.TSHashTable::TSHashTable();
	IsFirstFrame = FALSE;
};

//template <class THashKeyData, class TScriptData, class TConstantStringHashKeyManager>
//TFrame<THashKeyData,TScriptData,TConstantStringHashKeyManager>::~TFrame()
TFrame::~TFrame()
{
	Stack.~TFrameStack();
	Locals.~TSHashTable();
};

TJassArray::TJassArray()
{
	vtable = JassArray_VMT;
	List.TSArrayM::TSArrayM();
};

TJassArray::~TJassArray()
{
	List.TSArrayM<Dword,TDefaultArrayItemManager>::~TSArrayM<Dword,TDefaultArrayItemManager>();
};

template <typename T>
procedure TDefaultInstanceHandleTableItemManager<T>::SetupItem(T AItem,
	const T AKey)
{
	AItem = AKey;
};

template <typename T>
T TDefaultInstanceHandleTableItemManager<T>::ExtractItemKey(T AItem)
{
	return AItem;
};

template <typename T>
procedure TDefaultInstanceHandleTableItemManager<T>::ClearItem(T AItem)
{
	// nothing to do
};

procedure TJassStringInstanceHandleTableItemManager::SetupItem(
	TJassString AItem, const PChar AKey)
{
	AItem.SetContent(AKey);
};

PChar TJassStringInstanceHandleTableItemManager::ExtractItemKey(
	TJassString AItem)
{
	return AItem.GetContent();
};

procedure TJassStringInstanceHandleTableItemManager::ClearItem(
	TJassString AItem)
{
	AItem.SetContent(NULL);
};

template <typename TItem, typename TItemManager, typename TItemArrayManager, typename TKey, typename THashKeyManager>
TItem TInstanceHandleTable<TItem,TItemManager,TItemArrayManager,TKey,THashKeyManager>::GetItem(TItemHandle AIndex)
{
	return GetItem(AIndex);
};

template <typename TItem, typename TItemManager, typename TItemArrayManager, typename TKey, typename THashKeyManager>
TItem* TInstanceHandleTable<TItem,TItemManager,TItemArrayManager,TKey,THashKeyManager>::GetItemPointer(TItemHandle AIndex)
{
	return GetItemPointer(AIndex);
};

template <typename TItem, typename TItemManager, typename TItemArrayManager, typename TKey, typename THashKeyManager>
Dword TInstanceHandleTable<TItem,TItemManager,TItemArrayManager,TKey,THashKeyManager>::GetItemsCount()
{
	return GetItemsCount();
};

template <typename TItem, typename TItemManager, typename TItemArrayManager, typename TKey, typename THashKeyManager>
Dword TInstanceHandleTable<TItem,TItemManager,TItemArrayManager,TKey,THashKeyManager>::FindHandle(const TKey AKey)
{
	TItemHandle Result;
	PItemHandleNode pNode;

	Result = 0;

	if ((*FHandleByKeyTable).Find(AKey, pNode))
	{
		Result = (*pNode).Value;
	};
	return Result;
};

template <typename TItem, typename TItemManager, typename TItemArrayManager, typename TKey, typename THashKeyManager>
Dword TInstanceHandleTable<TItem,TItemManager,TItemArrayManager,TKey,THashKeyManager>::GetHandle(const TKey AKey, Boolean &Created)
{
	TItemHandle Result;
	PItemHandleNode pNode;
	PItem pNewItem;

	Created = FALSE;

	if (FHandleByKeyTable->Find(AKey, pNode))
	{
		Result = (*pNode).Value;
		return Result;
	};

	if (FFreeHandles->GetLength() > 0)
	{
		Result = FFreeHandles->Pop();
		pNewItem = FItems->GetItemPointers().GetItem(Result);
	}
	else
	{
		pNewItem = FItems->GetNewItem();
		Result = FItems->GetLength() - 1;
	};

	TItemManager::SetupItem((*pNewItem), AKey);

	pNode = (*FHandleByKeyTable).CreateItem(AKey);
	(*pNode).Value = Result;

	Created = TRUE;
	return Result;
};

template <typename TItem, typename TItemManager, typename TItemArrayManager, typename TKey, typename THashKeyManager>
Dword TInstanceHandleTable<TItem,TItemManager,TItemArrayManager,TKey,THashKeyManager>::GetHandle(const TKey AKey)
{
	Boolean Created;

	return GetHandle(AKey, Created);
};

template <typename TItem, typename TItemManager, typename TItemArrayManager, typename TKey, typename THashKeyManager>
procedure TInstanceHandleTable<TItem,TItemManager,TItemArrayManager,TKey,THashKeyManager>::FreeHandle(TItemHandle AHandle)
{
	TItem* Item;

	Item = GetItemPointer(AHandle);
	FHandleByKeyTable->Remove(TItemManager::ExtractItemKey((*Item)));
	TItemManager::ClearItem((*Item));
	FFreeHandles->Add(AHandle);
};

procedure TInstance::SetScriptDataValue(TScriptData AData, Dword ANewValue, TScriptDataType ANewType)
{
		if (AData.Value != 0)
		{
			switch (AData._Type)
			{
				case stHandle: DecrementHandleReferences(AData.Value); break;
				case stString: DecrementStringReferences(AData.Value); break;
			};
		};

		AData.Value = ANewValue;
		AData._Type = ANewType;

		if (ANewValue != 0)
		{
			switch (ANewType)
			{
				case stHandle: IncrementHandleReferences(ANewValue); break;
				case stString: IncrementStringReferences(ANewValue); break;
			};
		};
};

procedure TInstance::ChangeValueOfType(Dword ADestination,
	TScriptDataType AType, Dword AValue)
{
	switch (AType)
	{
		case stHandle:
			if (ADestination != 0) DecrementHandleReferences(ADestination);
			ADestination = AValue;
			if (ADestination != 0) IncrementHandleReferences(ADestination);
			break;

		case stString:
			if (ADestination != 0) DecrementStringReferences(ADestination);
			ADestination = AValue;
			if (ADestination != 0) IncrementStringReferences(ADestination);
			break;

		default: ADestination = AValue;
	};
};

procedure TInstance::ClearJassArrayItems(PJassArray JassArray, TScriptDataType ItemType)
{
	Integer vvi;

		for (vvi = 0; vvi <= (*JassArray).List.FLength - 1; vvi++)
		{
			if ((*JassArray).List.FItems[vvi] != 0)
			{
				switch (ItemType)
				{
					case stHandle: DecrementHandleReferences((*JassArray).List.FItems[vvi]); break;
					case stString: DecrementStringReferences((*JassArray).List.FItems[vvi]); break;
				};

				(*JassArray).List.FItems[vvi] = 0;
			};
		};
};

procedure TInstance::ClearScriptData(PScriptData Data)
{
		switch ((*Data)._Type)
		{
			case stHandle: DecrementHandleReferences((*Data).Value); break;
			case stString: DecrementStringReferences((*Data).Value); break;

			case stArrayOfHandle: if ((*Data).Value != 0)
				ClearJassArrayItems(PJassArray((*Data).Value), stHandle); break;

			case stArrayOfString: if ((*Data).Value != 0)
				ClearJassArrayItems(PJassArray((*Data).Value), stString); break;
		};

		(*Data)._Type = stVoid;
		(*Data).Value = 0;
};

procedure TInstance::IncrementHandleReferences(TJassObjectHandle hAgent)
{
	__asm
	{
		mov ecx, hAgent
		mov edx, mhaReference
	};
	RefHandleCallback(RefHandleCallbackParam);
};

procedure TInstance::DecrementHandleReferences(TJassObjectHandle hAgent)
{
	__asm
	{
		mov ecx, hAgent
		mov edx, mhaUnreference
	};
	RefHandleCallback(RefHandleCallbackParam);
};

procedure TInstance::IncrementStringReferences(TJassStringHandle hString)
{
	if (hString == 0) return;
	//(*((*(StringTable.FItems)).ItemPointers.Items[hString])).FReferences += 1;
	StringTable.FItems->GetItemPointers().GetItem(hString)->FReferences += 1;
};

procedure TInstance::DecrementStringReferences(TJassStringHandle hString)
{
	if (hString == 0) return;

		StringTable.FItems->GetItemPointers().GetItem(hString)->FReferences--;

		if (StringTable.FItems->GetItemPointers().GetItem(hString)->FReferences == 0)
		{
			StringTable.FreeHandle(hString);
		};
};

TInstance* TInstance::GetTopmostParent()
{
	PInstance Result;
	PJassThreadLocal Environment;

	Result = this;
	Environment = GetJassThreadLocal();

	while (Result->IsChild)
	{
		Result = Environment->Instances.FItems[Result->ParentHandle];
	};
		return Result;
};

procedure TJassString::SetContent(PChar AContent)
{
	FValue.SetContent(AContent);
};

PChar TJassString::GetContent()
{
	return FValue.GetContent();
};

TJassString::TJassString()
{
	FValue.TRCString::TRCString();
	FReferences = 0;
};

TJassString::~TJassString()
{
	FValue.~TRCString();
};

PChar THashedStringTable::GetItem(Dword AIndex)
{
	return (*(FItems.FItems[AIndex])).HashKeyData.Key;
};

Dword THashedStringTable::GetItemHash(Dword AIndex)
{
	return (*(FItems.FItems[AIndex])).HashKeyData.Hash;
};

Boolean THashedStringTable::GetItemIndex(PChar AItem, Dword &AIndex)
{
	Boolean Result;
	PHashedString HashedString;

	Result = FStrToItem.Find(AItem, HashedString);
	if (Result)
		AIndex = (*HashedString).Index;
	return Result;
};

Dword THashedStringTable::GetLength()
{
	return FItems.GetLength();
};

void wc3jassvm_main()
{
	wc3main_SubscribeOnInit(&wc3jassvm_OnInit);
}
